//
//  ES1Renderer.m
//  MSAATest
//
//  Created by Feng Ye on 10-8-5.
//  Copyright __MyCompanyName__ 2010. All rights reserved.
//

#import "ES1Renderer.h"


@implementation ES1Renderer


// Create an OpenGL ES 1.1 context
- (id)init
{
    if ((self = [super init]))
    {
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

        if (!context || ![EAGLContext setCurrentContext:context])
        {
            [self release];
            return nil;
        }

        // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
        glGenFramebuffersOES(1, &defaultFramebuffer);
        glGenRenderbuffersOES(1, &colorRenderbuffer);
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer);
		
		// msaa fb
		glGenFramebuffersOES(1, &msaaFramebuffer);
		// msaa color buffer
		glGenRenderbuffersOES(1, &msaaRenderbuffer);
		// msaa depth buffer
		glGenRenderbuffersOES(1, &msaaDepthbuffer);
        
        
        motionManager = [[CMMotionManager alloc]init];
        if(motionManager.deviceMotionAvailable){
            motionManager.deviceMotionUpdateInterval = .01;
            [motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical toQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
                
                if(motion){
                    CMAttitude *attitude = motion.attitude;
                    roll_degree = -RADIANS_TO_DEGREES(attitude.roll);
                    pitch_degree = -RADIANS_TO_DEGREES(attitude.pitch);
                    yaw_degree = -RADIANS_TO_DEGREES(attitude.yaw);
                }
            }];
        }
        
    }

    return self;
}

- (void) setupView{
    GLfloat size;
    glEnable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    size = kZNear * tanf(DEGREES_TO_RADIANS(kFieldOfView) / 2.0);
    glFrustumf(-size, size, -size / ((backingWidth + 0.0) / backingHeight), size /
               ((backingWidth + .0) / backingHeight), kZNear, kZFar);
    glViewport(0, 0, backingWidth, backingHeight);
    glMatrixMode(GL_MODELVIEW);
    
    
    lineSpace = 0.05;
    pointCount = 8/lineSpace + 4;
    vertices = malloc(sizeof(Vertex3D) * (pointCount + 4*4));
    float z = -0.1;
    
    int p = 0;
    for(float i = -1.0;i <1.0 + lineSpace;i+=lineSpace){
        Vertex3DSet(&vertices[p], i, -1, z);
        p++;
        Vertex3DSet(&vertices[p], i, 1, z);
        p++;
    }
    for (float j = -1.0; j <= 1.0 + lineSpace; j+=lineSpace) {
        Vertex3DSet(&vertices[p], -1, j, z);
        p++;
        Vertex3DSet(&vertices[p], 1, j, z);
        p++;
    }
    
    icosahedronFaces =  malloc(sizeof(GLubyte) * pointCount);
    for (int i = 0; i < pointCount; i++) {
        icosahedronFaces[i] = i;
    }
    
    // x
    Vertex3DSet(&vertices[pointCount], 1,-0.05, 0);
    Vertex3DSet(&vertices[pointCount+1], 1,-0.05, -0.1);
    Vertex3DSet(&vertices[pointCount+2], 1,0.05, 0);
    Vertex3DSet(&vertices[pointCount+3], 1,0.05, -0.1);
    
    //y
    Vertex3DSet(&vertices[pointCount+4], -0.05,1, 0);
    Vertex3DSet(&vertices[pointCount+5], -0.05,1, -0.1);
    Vertex3DSet(&vertices[pointCount+6], 0.05,1, 0);
    Vertex3DSet(&vertices[pointCount+7], 0.05,1, -0.1);
    
    // -x
    Vertex3DSet(&vertices[pointCount+8], -1,-0.05, 0);
    Vertex3DSet(&vertices[pointCount+9], -1,-0.05, -0.1);
    Vertex3DSet(&vertices[pointCount+10], -1,0.05, 0);
    Vertex3DSet(&vertices[pointCount+11], -1,0.05, -0.1);
    
    // -y
    Vertex3DSet(&vertices[pointCount+12], -0.05,-1, 0);
    Vertex3DSet(&vertices[pointCount+13], -0.05,-1, -0.1);
    Vertex3DSet(&vertices[pointCount+14], 0.05,-1, 0);
    Vertex3DSet(&vertices[pointCount+15], 0.05,-1, -0.1);
   
}

- (void)render{

    

    // This application only creates a single context which is already set current at this point.
    // This call is redundant, but needed if dealing with multiple contexts.
    [EAGLContext setCurrentContext:context];
    
    
    if (!viewSetup) {
        [self setupView];
        viewSetup = YES;
    }
    
    

	// Choose framebuffer if it comes to MSAA
    if ( gMSAAEnabled )
	{
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer);
	}
	else {
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer);
	}
	
    
    glLoadIdentity();
        
    glRotatef(roll_degree, 0.0, 1.0, 0.0);   //y
    glRotatef(pitch_degree, 1.0, 0.0, 0.0);  //x
    glRotatef(yaw_degree, 0.0, 0.0, 1.0);
    
    
    
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnableClientState(GL_VERTEX_ARRAY);
	glEnable(GL_COLOR_MATERIAL);
    
    
    /**/
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    
    //glColorPointer(4, GL_FLOAT, 0, colors);
    glColor4f(.6, .6, .6, 1.0);
    glDrawElements(GL_LINES, pointCount, GL_UNSIGNED_BYTE, icosahedronFaces);
    
    // x
    glColor4f(.6, .0, .0, 1.0);
    glDrawArrays(GL_TRIANGLE_STRIP, pointCount, 4);
    
    // y
    glColor4f(.0, .6, .0, 1.0);
    glDrawArrays(GL_TRIANGLE_STRIP, pointCount + 4, 4);
    
    // -x
    glColor4f(.0, .0, .6, 1.0);
    glDrawArrays(GL_TRIANGLE_STRIP, pointCount + 4*2, 4);
    
    // -y
    glColor4f(.0, .6, .6, 1.0);
    glDrawArrays(GL_TRIANGLE_STRIP, pointCount + 4*3, 4);
    /**/
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_COLOR_MATERIAL);

    
    
    
    
	
	// resolve the final pixels if MSAA enabled
	if ( gMSAAEnabled )
	{
		glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, msaaFramebuffer);
		glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, defaultFramebuffer);
		glResolveMultisampleFramebufferAPPLE();
	}
	
	// discard depth buffer whenever possible, to gain more memory bandwidth.
	// this is no necessary for MSAA but helps boost performance even in non-MSAA cases.
	GLenum attachments[] = {GL_DEPTH_ATTACHMENT_OES};
	glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, attachments);

    // need to restore colorRenderbuffer if it's MSAA enabled
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer
{	
    // Allocate color buffer backing based on the current layer size
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

    if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
    {
        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }

	GLint maxSamplesAllowed;
	glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed);
	
	// operation on msaa fb
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer);
	
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, msaaRenderbuffer);
	glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, maxSamplesAllowed, GL_RGBA8_OES, backingWidth, backingHeight);
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, msaaRenderbuffer);
	
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, msaaDepthbuffer);
	glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, maxSamplesAllowed, GL_DEPTH_COMPONENT24_OES, backingWidth, backingHeight);
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, msaaDepthbuffer);
	
	if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
    {
        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }
	
    return YES;
}

- (void)dealloc{
    // Tear down GL
    if (defaultFramebuffer)
    {
        glDeleteFramebuffersOES(1, &defaultFramebuffer);
        defaultFramebuffer = 0;
    }

    if (colorRenderbuffer)
    {
        glDeleteRenderbuffersOES(1, &colorRenderbuffer);
        colorRenderbuffer = 0;
    }
	
	if ( msaaFramebuffer )
	{
		glDeleteFramebuffersOES(1, &msaaFramebuffer);
		msaaFramebuffer = 0;
	}
	if ( msaaRenderbuffer )
	{
		glDeleteRenderbuffersOES(1, &msaaRenderbuffer);
		msaaRenderbuffer = 0;
	}
	if ( msaaDepthbuffer )
	{
		glDeleteRenderbuffersOES(1, &msaaDepthbuffer);
		msaaDepthbuffer = 0;
	}
	
    [motionManager stopDeviceMotionUpdates];
    [motionManager release];
    
    free(vertices);
    free(icosahedronFaces);

    // Tear down context
    if ([EAGLContext currentContext] == context)
        [EAGLContext setCurrentContext:nil];

    [context release];
    context = nil;

    [super dealloc];
}

@end
